package tech.treec.smartlight.service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import androidx.core.app.NotificationCompat;
import androidx.preference.PreferenceManager;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.young.mylib.db.dao.DeviceDao;
import com.young.mylib.db.entity.DeviceDataEntity;
import com.young.mylib.db.entity.DeviceEntity;
import com.young.mylib.db.entity.LinkageEntity;
import com.young.mylib.db.entity.LinkageLog;
import com.young.mylib.utils.ConverterUtils;
import com.young.mylib.utils.JsonUtils;
import com.young.mylib.utils.TimeUtils;

import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallbackExtended;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttPersistenceException;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;

import tech.treec.smartlight.R;

import static tech.treec.smartlight.Constants.ACTION_MQTT_PUB_MSG;
import static tech.treec.smartlight.Constants.ACTION_MQTT_SUB_MSG;
import static tech.treec.smartlight.Constants.ACTION_MQTT_SUB_MSG_RECEIVE;
import static tech.treec.smartlight.Constants.DEVICE_TYPE_AIR;
import static tech.treec.smartlight.Constants.DEVICE_TYPE_BODY;
import static tech.treec.smartlight.Constants.DEVICE_TYPE_LIGHT;

public class MQTTService extends Service implements MqttCallbackExtended {
    private static final String TAG = "MQTTService";
    private Timer serviceTimer = null;
    private MqttMsgReceiver msgReceiver;
    private static final String CLIENT_ID = "smartLightClientId";
    private static MqttClient mqttClient;
    MqttConnectOptions connOpts;
    private Looper mqttConnectLooper;
    private MqttConnectHandler mqttConnectHandler;

    public MQTTService() {
    }

    private final class MqttConnectHandler extends Handler {
        public MqttConnectHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            try {
                connect();
            } catch (MqttException | IOException e) {

                e.printStackTrace();
            }
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "mqtt 服务建立");
        // reset status variable to initial state

        msgReceiver = new MqttMsgReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(ACTION_MQTT_SUB_MSG);
        intentFilter.addAction(ACTION_MQTT_PUB_MSG);
        registerReceiver(msgReceiver, intentFilter);
        serviceTimer = new Timer();
        serviceTimer.schedule(timerTask, 6000, 1000 * 30);

        /**
         * 开一个线程连接mqtt
         */
        HandlerThread thread = new HandlerThread("ServiceStartArguments", 0);
        thread.start();

        mqttConnectLooper = thread.getLooper();
        mqttConnectHandler = new MqttConnectHandler(mqttConnectLooper);

    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onStart(final Intent intent, final int startId) {
        Log.i(TAG, "onStart");
    }

    @Override
    public int onStartCommand(final Intent intent, int flags, final int startId) {
        Log.i(TAG, "onStartCommand");
        startForeground(1, getNotfication());
        Message msg = mqttConnectHandler.obtainMessage();
        msg.arg1 = startId;
        mqttConnectHandler.sendMessage(msg);
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        try {
            disconnect();
            if (msgReceiver != null) {
                unregisterReceiver(msgReceiver);
            }
        } catch (MqttException e) {
            e.printStackTrace();
        }

    }

    private Notification getNotfication() {
        String channelId = "smart_light_channel";
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(channelId, "SMART LIGHT CHANNEL", NotificationManager.IMPORTANCE_DEFAULT);
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(channel);
        }
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
                .setContentTitle("Service Running")
                .setContentText("MyService is running in the foreground")
                .setSmallIcon(R.drawable.app);
        return builder.build();

    }

    TimerTask timerTask = new TimerTask() {
        @Override
        public void run() {
            Log.i(TAG, "MQTT is connected?" + mqttClient.isConnected() + " ->" + mqttClient.getCurrentServerURI());

            if (mqttClient.isConnected()) {
                try {
                    heartbeat();
                } catch (MqttException e) {
                    e.printStackTrace();
                }
            }

        }
    };

    @Override
    public void connectionLost(Throwable cause) {
        Log.i(TAG, "MQTT CONNECT LOST");
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        // Log.i(TAG, "MQTT 消息到达：" + topic + ":" + message);

//        Intent intent = new Intent();
//        intent.putExtra("topic", topic);
//        intent.putExtra("payload", message.toString());
//        intent.setAction(ACTION_MQTT_SUB_MSG_RECEIVE);
//
//        sendBroadcast(intent);
        SharedPreferences prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        String panelId = prefs.getString("panelId", "588");

        JSONObject json = JsonUtils.toJsonObject(message.toString());
        if (json == null) return;

        if (topic.contains("data/report/body")) {
            for (Map.Entry<String, Object> entry : json.entrySet()) {
                String deviceId = entry.getKey();
                Object obj = entry.getValue();
                DeviceDataEntity deviceDataEntity = DeviceDao.findRealDataByKey("deviceId", deviceId);
                Log.i(TAG, "########### 检查无人关灯");
                checkBody(deviceDataEntity, obj);
                saveDeviceData(deviceId, obj, DEVICE_TYPE_BODY);
            }
        }
        if (topic.contains("data/report/air")) {
            for (Map.Entry<String, Object> entry : json.entrySet()) {
                String deviceId = entry.getKey();
                Object obj = entry.getValue();
                saveDeviceData(deviceId, obj, DEVICE_TYPE_AIR);
            }
        }
        if (topic.contains("data/report/light")) {
            for (Map.Entry<String, Object> entry : json.entrySet()) {
                String deviceId = entry.getKey();
                Object obj = entry.getValue();
                saveDeviceData(deviceId, obj, DEVICE_TYPE_LIGHT);
            }
        }
        if (topic.equals(panelId + "/cmd")) {

        }
        if (topic.equals("/light/DARMAO000" + panelId + "0000/data/report")) {

        }
    }

    private void saveDeviceData(String deviceId, Object obj, String deviceType) {
        long ts = TimeUtils.getCurrTs();
        String time = TimeUtils.getCurrentTimeAnd8();
        DeviceEntity device = DeviceDao.findDevice(deviceId);
        if (device != null) {
            DeviceDataEntity deviceDataEntity = new DeviceDataEntity();
            deviceDataEntity.deviceId = deviceId;
            deviceDataEntity.deviceName = device.deviceName;
            deviceDataEntity.deviceType = deviceType;
            deviceDataEntity.lightStatus = ConverterUtils.str2int(obj.toString(), 0);
            deviceDataEntity.time = time;
            deviceDataEntity.ts = ts;

            DeviceDao.saveDeviceData(deviceDataEntity);
            DeviceDao.updateDeviceRealtimeData(deviceDataEntity);

        }
    }


    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {

    }

    public void connect() throws MqttException, MqttPersistenceException, IOException {

        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

        String brokerHostName = prefs.getString("mqttUrl", "10.8.5.54");
        String port = prefs.getString("mqttPort", "1883");
        String panelId = prefs.getString("panelId", "588");
        String url = "tcp://" + brokerHostName + ":" + port;
        Log.i(TAG, "连接MQTT：" + url);
        mqttClient = new MqttClient(url, CLIENT_ID + panelId, new MemoryPersistence());
        connOpts = new MqttConnectOptions();
//        connOpts.setUserName(username);
//        connOpts.setPassword(password.toCharArray());
//        connOpts.setCleanSession(true);
//        connOpts.setMaxInflight(2048);
//        connOpts.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);

        connOpts.setAutomaticReconnect(true);
        connOpts.setCleanSession(true);
        connOpts.setKeepAliveInterval(60);

        mqttClient.setCallback(this);
        mqttClient.connect(connOpts);

    }

    private void heartbeat() throws MqttException {
        //   Log.i(TAG, "heartbeat:" + isAlreadyConnected());
        if (mqttClient.isConnected()) {
            SharedPreferences prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(this);
            String panelId = prefs.getString("panelId", "588"); // 获取默认值作为备选方案。
            String topic = panelId + "/heartbeat";
            JSONObject json = new JSONObject();
            json.put("panelId", panelId);

            long ts = System.currentTimeMillis();
            Date time = new Date(ts);
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            format.setTimeZone(TimeZone.getTimeZone("GMT+8"));
            json.put("time", format.format(time));

            publish(topic, json.toJSONString());
        }
    }

    public static void subscribe(String topic) throws MqttException {
        if (mqttClient == null || !mqttClient.isConnected()) {
            Log.i(TAG, "订阅topic失败，mqtt未连接：" + topic);
            return;
        }
        mqttClient.subscribe(topic);
        //     Log.i(TAG, "订阅topic：" + topic);
//        if (mqttClient == null || !mqttClient.isConnected()) {
//            cacheTopics.add(topic);
//            return;
//        }
//        if (!topics.contains(topic)) {
//            mqttClient.subscribe(topic);
//            topics.add(topic);
//            Log.i(TAG, "订阅topic：" + topic);
//        }else {
//            Log.i(TAG, "订阅topic，已订阅：" + topic);
//        }
    }

    public static void unsubscribe(String topic) throws MqttException {
        if (mqttClient != null || mqttClient.isConnected())
            mqttClient.unsubscribe(topic);
        Log.i(TAG, "onReceive 取消订阅 " + topic);
    }

    public static void publish(String topic, String message) throws MqttException {
        MqttMessage mqttMessage = new MqttMessage(message.getBytes());
        mqttClient.publish(topic, mqttMessage);
    }

    public static void disconnect() throws MqttException {

        if (mqttClient == null) return;
        if (mqttClient.isConnected()) {
            mqttClient.disconnect();
            mqttClient.close();
        }
    }


    @Override
    public void connectComplete(boolean reconnect, String serverURI) {
        Log.i(TAG, "mqtt连接成功,isReconnet?" + reconnect + ":" + serverURI);

        SharedPreferences prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        String panelId = prefs.getString("panelId", "588");
        String bodyTopic = "/light/DARMAO000" + panelId + "0000/data/report/body";//人体感应
        String lightTopic = "/light/DARMAO000" + panelId + "0000/data/report/light";//照明
        String airTopic = "/light/DARMAO000" + panelId + "0000/data/report/air";//照明
        String subTopic = "/light/DARMAO000" + panelId + "0000/data/report";//用来记录开关日志
        String cmd = panelId + "/cmd";
        try {
            subscribe(subTopic);
            subscribe(bodyTopic);
            subscribe(lightTopic);
            subscribe(airTopic);
            subscribe(cmd);
        } catch (MqttException e) {
            Log.e(TAG, "订阅出错：" + e.getMessage());
            e.printStackTrace();
        }
    }

    private class MqttMsgReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            if (intent.getAction().equals(ACTION_MQTT_SUB_MSG)) {
                try {
                    String topic = intent.getStringExtra("topic");
                    boolean isSub = intent.getBooleanExtra("isSub", true);

                    if (isSub) {
                        subscribe(topic);
                    } else {
                        unsubscribe(topic);
                    }

                } catch (Exception e) {
                    Log.e(TAG, "MsgReceiver error: " + e.getMessage());
                }
            }

            if (intent.getAction().equals(ACTION_MQTT_PUB_MSG)) {
                String topic = intent.getStringExtra("topic");
                String payload = intent.getStringExtra("payload");
                Log.i(TAG, "MQTT PUB " + topic);
                try {
                    publish(topic, payload);
                } catch (MqttException e) {
                    Log.e(TAG, "MQTT PUB " + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }

    private void checkBody(DeviceDataEntity lastData, Object obj) {
        int status = ConverterUtils.str2int(obj.toString(), 1);
        if (status == 1) return;
        String treeId = lastData.treeId;
        LinkageEntity linkageEntity = DeviceDao.findLinkage("treeId", treeId);
        JSONArray devices = linkageEntity.devices;
        boolean isEnabled = linkageEntity.isEnabled;
        boolean hasAction = false;
        List<String> airDevices = new ArrayList<>();
        List<String> lightDevices = new ArrayList<>();
        List<String> bodyDevices = new ArrayList<>();

        if (isEnabled && devices.size() > 0) {
            for (Object dev : devices) {
                DeviceEntity device = JsonUtils.toBean(dev.toString(), DeviceEntity.class);
                String deviceId = device.deviceId;
                DeviceDataEntity lightDeviceRealtimeData = DeviceDao.findRealDataByKey("deviceId", deviceId);
                if (getDeviceType(deviceId).equals(DEVICE_TYPE_BODY)) {
                    bodyDevices.add(deviceId);
                } else if (getDeviceType(deviceId).equals(DEVICE_TYPE_LIGHT)) {
                    if (lightDeviceRealtimeData.lightStatus == 1) {
                        hasAction = true;
                    }
                    lightDevices.add(deviceId);
                } else {
                    if (lightDeviceRealtimeData.lightStatus == 1) {
                        hasAction = true;
                    }
                    airDevices.add(deviceId);
                }
            }
        }

        if (!hasAction) return;


        String time = TimeUtils.getCurrentTimeAnd8();
        long lastTs = TimeUtils.getCurrTsBeforeMinute(-3);
        String lastTime = TimeUtils.getTimeAnd8(lastTs);

        List<DeviceDataEntity> list = DeviceDao.findDataByRangeAndKey(lastTs, "deviceId", lastData.deviceId);

        if (lastData.lightStatus == status) {//如果上次也为0，关灯
            long ts = TimeUtils.getCurrTs();

            LinkageLog linkageLog = new LinkageLog();
            linkageLog.time = time;
            linkageLog.ts = ts;
            linkageLog.lastTime = lastData.time;
            linkageLog.name = lastData.thingName;
            linkageLog.deviceName = lastData.deviceName;
        }
    }

    public static String getDeviceType(String deviceId) {
        int num = indexOfStr(deviceId, '_');
        switch (num) {
            case 0:
                return DEVICE_TYPE_AIR;
            case 1:
                return DEVICE_TYPE_BODY;
            case 2:
                return DEVICE_TYPE_LIGHT;
            default:
                return "";
        }
    }

    private static int indexOfStr(String str, char targetChar) {
        int count = 0;

        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == targetChar) {
                count++;
            }
        }
        return count;
    }
}

